const FUN = require('../comm/func');
const rCache = require('../model/redis_cache');
const timLib = require('../comm/time-lib');
const PMR=require('../utils/policyManger');
const DB = require('../model/model_data');
const dataCalc = require('../comm/data-calc');

// 量化 - 实时价格计算
module.exports = class Futures_Hedge
{
  // 将数据库选项缓存到redis
  // policy 来自数据库的记录，附加了user_auth,user_level字段
  // opts 数据库json字段的解析对象
  // CacheCallback 存储执行函数
  static InitCache(policy, opts, CacheCallback)
  {
    let dConf = PMR.baseParam(policy); // 即将缓存到redis的参数

    // 用模板参数替换配置项内容
    if (opts.args && opts.args != 'undefined') {
      let argsArr = opts.args.split(',');
      for (let i in argsArr) { // 将#1,#2...替换
        let argStr = new RegExp(`#${parseInt(i)+1}`,'g'); // 用正则的方式可以替换所有匹配
        let realArg = argsArr[i].split('@')[0]; // 去掉注释部分
        opts.topics = opts.topics.replace(argStr, realArg);
        opts.vars = opts.vars.replace(argStr, realArg);
        opts.calc = opts.calc.replace(argStr, realArg);
      }
    }

    pcyLog(`[${dConf.policy}][${dConf.policy_id}][${dConf.policy_name}] topics`, opts.topics);

    // 缓存参数
    dConf.topics = opts.topics; // 主题列表，分号分隔
    dConf.vars = opts.vars; // 变量列表，分号分隔
    dConf.calc = opts.calc; // 算式列表，|分隔
    dConf.time_scope = opts.time_scope; // 时间段字符串
    dConf.period = opts.period;
    dConf.record_limit = policy.record_limit; // 数据库条数上限
    dConf.record_duration = 0; // 数据库周期上限
    dConf.tidy_period = 100;

    // 订阅用户主题
    let funcList = [];
    let topicList = opts.topics.split(',');
    for (let i = 0; i < topicList.length; i++) {
      let tArr = topicList[i].split('/');
      funcList.push(tArr[1]);
      PMR.subMqttTopic(topicList[i], policy.type, policy.id);
    }
    PMR.subMqttTopic('forward/sync', policy.type, policy.id); // 同步信号

    dConf.funcs = funcList.join(',');
    CacheCallback(policy.id, policy.master, dConf); // 记录到缓存
    rCache.setKey(PMR.rdNode(dConf), 'count', 0); // 计数器
  } // end InitCache

  // 策略被删除
  // policy 来自数据库的记录
  static Exit(policy, opts)
  {
    pcyLog(`[${policy.type}][${policy.id}][${policy.name}] exit topics`, opts.topics);

    // 取消主题订阅，必须与init时对应，否则会产生冗余订阅
    let topicList = opts.topics.split(',');
    for (let i = 0; i < topicList.length; i++) {
      PMR.cancleMqttTopic(topicList[i], policy.type, policy.id);
    }
    PMR.cancleMqttTopic('forward/sync', policy.type, policy.id); // 同步信号
  }

  // 获取一个节点下所有子节点并将其保存成obj数组输出
  static async GetNodeToObj(path, filter=null)
  {
    let nodes = await rCache.keys(path, '*');
    if (!nodes) return {};

    let retObj = {};
    for(let i = 0; i < nodes.length; i++) { // 轮询每个子节点
      let p = nodes[i].split(':');
      let name = p[p.length-1]; // 提取节点名
      if (filter && !filter.includes(name)) {
        continue;
      }
      retObj[name] = await rCache.getAll(nodes[i]);
    }
    return retObj;
  }

  // 处理每个变量算式的计算
  static VarCalcHander(calcStr, prices, dConf=null)
  {
    let scale = 2; // 默认2位小数点
    let vars = calcStr.match(/[$][A-Za-z0-9.()]+[$]/g);
    if (vars) {
      for (let i = 0; i < vars.length; i++) {
        let varName = vars[i]; // 获取到带$的变量名

        let scaleStr = varName.match(/[(][0-9]+[)]/g); // 查找是不是有指定小数位
        if (scaleStr) scale = scaleStr[0].replace(/[()]/g, ''); // 去掉首尾括号得到小数位

        varName = varName.replace(/[(][0-9]+[)]/g, ''); // 去掉名字的括号内容
        calcStr = calcStr.replace(vars[i], varName); // 去掉算式里面的括号

        varName = varName.replace(/[$]/g, ''); // 去掉首尾$S
        varName = varName.split('.'); // 分隔尾缀
        if (!Object.keys(prices).includes(varName[0])) return 0; // prices中缺少对象值，无法处理
        if (varName.length == 1) {
          if (!isVaild(prices[varName[0]].NOW)) return 0;
          let matchReg = new RegExp(`[$]${varName[0]}[$]`, 'g');
          calcStr = calcStr.replace(matchReg , prices[varName[0]].NOW);
        } else if (varName.length == 2) {
          let matchReg = new RegExp(`[$]${varName[0]}[.]${varName[1]}[$]`, 'g');
          if (!isVaild(prices[varName[0]][varName[1]])) return 0;
          calcStr = calcStr.replace(matchReg , prices[varName[0]][varName[1]]);
        }
      } // end for
    } // end if vars

    return Number(eval(calcStr)).toFixed(scale); // 通过算式计算数值
  }

  // 处理数据
  // topic = {project,devType,func,devId}
  // dConf即InitCache中的obj
  // dataBuf即mq消息内容，buf对象
  static async DataIn(topic, dConf, DataBuf)
  {
    if (!timLib.NowTimeInScope(dConf.time_scope)) return; // 超过监控时间段了

    let stamp = timLib.NowStamp();
    let funcList = dConf.funcs.split(','); // 品种列表

    if (topic.func == 'sync') {
      let count = await rCache.getKey(PMR.rdNode(dConf), 'count');
      if (dConf.period != 0 && parseInt(count) % parseInt(dConf.period) != 0) {
        rCache.incKey(PMR.rdNode(dConf), 'count'); // 计数自增
        return;
      }

      let priceObjs = await this.GetNodeToObj(PMR.rdNode(dConf), funcList); // 获取到所有价格信息
      let splitChar = ','; // 变量和算式分隔符
      if (dConf.vars.includes(';')) splitChar = ';'; // 支持分号分隔符
      let varList = dConf.vars.split(splitChar);
      let calcList = dConf.calc.split(splitChar);

      let varsObj = {};
      for (let i = 0; i < calcList.length; i++) { // 处理每个算式
        let varName = varList[i];
        let judgeMathod = null;
        let judgeVal = 0;
        let calcValue = this.VarCalcHander(calcList[i], priceObjs, dConf);

        const mathod = ['==', '!=', '>=', '<=', '>', '<'];
        for (let m of mathod) {
          if (varList[i].includes(m)) {
            judgeMathod = m;
            varName = varList[i].split(m)[0];
            judgeVal = varList[i].split(m)[1];
            break;
          }
        }
        switch(judgeMathod) { // 判断算式
          case '==':

            break;
          case '!=':
            break;
        }

        varsObj[varName] = calcValue; // 赋值到对象进行后续存储
        // pcyLog('calc', varList[i], varsObj[varList[i]]);
      }

      // 存数据到db
      rCache.setKey(PMR.rdNode(dConf), 'db_stamp', timLib.NowStamp(), 60); // 记录时间戳
      DB.newRcd(dConf.uid, 'dat_json', 'hwid,policy_id,addr,json,stamp',
        [dConf.hwid, dConf.policy_id, count%2, JSON.stringify(varsObj), stamp]).then((insertId)=>{
          PMR.policyInform('update', dConf); // 通知前端
        })

      // 删除历史冗余
      if (parseInt(count) % parseInt(dConf.tidy_period) == 0) {
        dataCalc.dbRecordTidy(dConf.uid, dConf.table, dConf.policy_id, dConf.record_limit);
      }
      rCache.incKey(PMR.rdNode(dConf), 'count'); // 计数自增
    } else if (funcList.includes(topic.func)) {
      let priceObj = JSON.parse(DataBuf.toString());
      rCache.set(PMR.rdNode(dConf), topic.func, priceObj, 86400); // 缓存价格信息
    }

    return;
  } // end if (topic.func == 'sync')

}